﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Konves.Scripture.Data;

namespace Konves.Scripture
{
    /// <summary>
    /// Represents a single scripture verse.
    /// </summary>
    public class Verse
    {
        internal static Repository _repository;

        internal static Repository db
        {
            get
            {
                return _repository ?? (_repository = new Repository());
            }
        }

        internal static Verse Create(string book, int chapter, int verse, string suffix, VersePosition position, RepositoryMode mode)
        {
            Verse result = new Verse();
            
            BookInfo bi = db.GetBookInfo(book);

            if (bi == null)
            {
                result = null;
            }
            else
            {
                switch (position)
                {
                    case VersePosition.First:
                        if (chapter < 1)
                        {
                            if (chapter == -1 || mode == RepositoryMode.Optimistic)
                            {
                                chapter = 1;
                                verse = -1;
                            }
                            else
                                result = null;
                        }

                        if (result != null && verse < 1)
                        {
                            if (verse == -1 || mode == RepositoryMode.Optimistic)
                                verse = 1;
                            else
                                result = null;
                        }
                        break;
                    case VersePosition.Last:
                        if (chapter < 1)
                        {
                            if (chapter == -1 || mode == RepositoryMode.Optimistic)
                            {
                                chapter = db.GetMaxChapter(bi.Id);
                                verse = -1;
                            }
                            else
                                result = null;
                        }

                        if (result != null && verse < 0)
                        {
                            if (verse == -1 || mode == RepositoryMode.Optimistic)
                            {
                                try
                                {
                                    verse = db.GetMaxVerse(bi.Id, chapter);
                                }
                                catch (Exception)
                                {
                                    if (mode == RepositoryMode.Optimistic)
                                    {
                                        chapter = db.GetMaxChapter(bi.Id);
                                        verse = db.GetMaxVerse(bi.Id, chapter);
                                    }
                                    else
                                        result = null;

                                }
                            }
                            else
                                result = null;
                        }

                        break;
                }

                int maxChapter = db.GetMaxChapter(bi.Id);
                if (chapter > maxChapter)
                {
                    if (mode == RepositoryMode.Optimistic)
                    {
                        chapter = maxChapter;
                        verse = int.MaxValue;
                    }
                    else
                        result = null;
                }
                if (result != null)
                {
                    int maxVerse = db.GetMaxVerse(bi.Id, chapter);
                    if (verse > maxVerse)
                    {
                        if (mode == RepositoryMode.Optimistic)
                            verse = maxVerse;
                        else
                            result = null;
                    }
                }

                if (result != null)
                {

                    int index = -1;
                    int chapterResult = -1;
                    int verseResult = -1;

                    db.GetVerseData(bi.Id, chapter, verse, out index, out chapterResult, out verseResult);

                    result.BookName = bi.Name;
                    result.BookId = bi.Id;
                    result.Index = index; 
                    result.ChapterNumber = chapterResult; 
                    result.VerseNumber = verseResult; 
                    result.Suffix = string.IsNullOrWhiteSpace(suffix) ? null : suffix.Trim().ToLower();
                }

            }

            return result;
        }
        
        /// <summary>
        /// Gets the verse index.
        /// </summary>
        /// <value>
        /// The index.
        /// </value>
        public int Index { get; internal set; }

        /// <summary>
        /// Gets the name of the book.
        /// </summary>
        /// <value>
        /// The book.
        /// </value>
        public string BookName { get; internal set; }

        /// <summary>
        /// Gets the book id.
        /// </summary>
        /// <value>
        /// The book id.
        /// </value>
        public int BookId { get; internal set; }

        /// <summary>
        /// Gets the chapter number.
        /// </summary>
        /// <value>
        /// The chapter.
        /// </value>
        public int ChapterNumber { get; internal set; }

        /// <summary>
        /// Gets the verse number.
        /// </summary>
        /// <value>
        /// The verse.
        /// </value>
        public int VerseNumber { get; internal set; }

        /// <summary>
        /// Gets an optional verse suffix.
        /// </summary>
        /// <value>
        /// The suffix.
        /// </value>
        public string Suffix { get; internal set; }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.BookName + " " + this.ChapterNumber + ":" + this.VerseNumber + this.Suffix;
        }

        ///// <summary>
        ///// Implements the operator ==.
        ///// </summary>
        ///// <param name="v1">The v1.</param>
        ///// <param name="v2">The v2.</param>
        ///// <returns>
        ///// The result of the operator.
        ///// </returns>
        //public static bool operator ==(Verse v1, Verse v2)
        //{
        //    return v1.Equals(v2);
        //}

        ///// <summary>
        ///// Implements the operator !=.
        ///// </summary>
        ///// <param name="v1">The v1.</param>
        ///// <param name="v2">The v2.</param>
        ///// <returns>
        ///// The result of the operator.
        ///// </returns>
        //public static bool operator !=(Verse v1, Verse v2)
        //{
        //    return !v1.Equals(v2);
        //}

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator <(Verse v1, Verse v2)
        {
            return v1.Index < v2.Index;
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator >(Verse v1, Verse v2)
        {
            return v1.Index > v2.Index;
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            // TODO: implement in an IEqualityComparer
            return this.GetHashCode() == obj.GetHashCode();
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            // TODO: implement in an IEqualityComparer
            return
                ((this.Index * 100) >> 4)
                ^ ((this.BookId * 50) << 4)
                ^ (this.BookName == null ? 0 : this.BookName.GetHashCode())
                ^ (this.ChapterNumber >> 3)
                ^ (this.VerseNumber << 3)
                ^ (this.Suffix == null ? 0 : this.Suffix.GetHashCode());
        }
    }
}
